use common_base::prelude::*;

use crate::{
    cluster::EaiServer,
    data::{DataObject, ParamObject, ResponseObject},
    service_engine::ServiceComponentManager,
};

use super::{
    invoker::{InvokerName, RemoteInvoker, ServiceComponentInvoker},
    Invoker,
};

pub struct InvokerManager;

impl InvokerManager {
    pub fn initialize(service_container_dir: &str, service_dylib_dir: &str) {
        ServiceComponentManager::initialize(service_container_dir, service_dylib_dir);
    }

    pub async fn svr(
        service_key: &str,
        mut param_object: ParamObject,
        data_object: Option<DataObject>, /* custom_object, addon_object */
    ) -> Result<ResponseObject> {
        param_object.set_value_by_param_name("ServiceKey", service_key.to_owned());
        param_object.set_value_by_env_name("ServiceKey", service_key.to_owned());

        Self::iom(
            InvokerName::ServiceComponentInvoker,
            param_object,
            data_object,
        )
        .await
    }

    pub async fn iom_as_svr(
        object_name: &str,
        method_name: &str,
        param_object: ParamObject,
        data_object: Option<DataObject>,
        /* custom_object, addon_object */
    ) -> Result<ResponseObject> {
        // TODO: if object_name has `.`, this is remote invoke, should use RemoteInvoker wrap concrete Invoker( must do? )

        match (
            object_name.to_lowercase().as_str(),
            method_name.to_lowercase().as_str(),
        ) {
            // ("dbmanagerobject", "getdbconnection") => {
            //     Self::iom(InvokerName::DbConnManagerInvoker, param_object, data_object).await
            //     // .and_then(|r| r.try_into_ro())
            // }
            _ => Err(ErrorCode::InvokerNotFound(format!(
                "can not find ServicableInvoker for objectName: {}, methodName: {}",
                object_name, method_name
            ))),
        }
    }

    pub(crate) async fn iom(
        invoker: InvokerName,
        param_object: ParamObject,
        data_object: Option<DataObject>, /* custom_object, addon_object */
    ) -> Result<ResponseObject> {
        // Remote Invoke if necessary
        if let Some(eai_server) = param_object.eai_server.as_ref() {
            if !EaiServer::get_local_eai_server_id().eq(eai_server) {
                return RemoteInvoker {
                    eai_server: eai_server.to_owned(),
                    active_invoker_name: invoker.to_string(),
                }
                .call_method(param_object, data_object)
                .await;
            }
        }

        // Local Invoke
        match invoker {
            InvokerName::ServiceComponentInvoker => {
                ServiceComponentInvoker
                    .call_method(param_object, data_object)
                    .await
            }
            _ => todo!(),
        }
    }
}
